package se.webbzon.boltzmann.game.object.waypoint;

import java.awt.Color;

import javax.vecmath.Point3d;

import se.webbzon.boltzmann.environment.AbstractEnvironment;
import se.webbzon.boltzmann.game.switchable.Switchable;
import se.webbzon.oschi01.path3d.Path3D;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldeditor.DeflatableWorldObject;
import se.webbzon.oschi01.worldeditor.WorldObjectDeflation;
import se.webbzon.oschi01.worldobject.WorldMovableObject;
import se.webbzon.oschi01.worldobject.WorldObject;
import se.webbzon.oschi01.worldobject.appearance.Appearance3D;
import se.webbzon.oschi01.worldobject.appearance.EllipsoidAppearance3D;
import se.webbzon.oschi01.worldobject.appearance.NoAppearance3D;

public class Target extends WorldMovableObject implements PathMovable, Switchable, DeflatableWorldObject {
	
	// The name of this deflatable world object
	public static final String worldObjectName = "target";
	
	private enum State {PLAY, STOP, REWIND};
	
	// A point used to calculate the velocity of the target
	private final Point3d dP;
	
	// The state of the target
	private State state;
	
	// The current time and the duration of a program cycle
	private int time, duration;
	
	// The path which the target will follow
	private Path3D path;	
	
	// The waypoint used to generate the path of the target
	private Waypoint waypoint;
	
	// The current program enumeration of the target
	private PathProgramEnum programEnum;
	private PathProgram program;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public Target(AbstractEnvironment env) {
		this(env,PathProgramEnum.STATIC,null);
	}

	public Target(AbstractEnvironment env, PathProgramEnum programEnum, Waypoint waypoint) {
		super(WorldObject.onStep, createAppearance(env));
		setProgram(programEnum);
		setWaypoint(waypoint);
		dP = new Point3d();
		time = 0;
		duration = 1;
		state = State.PLAY;
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Sets the path of this platform by using the given waypoint. **/
	public void setWaypoint(Waypoint waypoint) {
		this.waypoint = waypoint;
		if (waypoint != null)
			path = waypoint.makePath3D();
	}
	
	/** Returns the waypoint used to create the path of the platform. **/
	public Waypoint getWaypoint() {
		return waypoint;
	}
	
	/** Sets the duration (in number of steps) of a full program cycle. 
	 * The duration must be greater or equal to 1 step. **/
	public void setCycleDuration(int numberOfSteps) {
		duration = (numberOfSteps < 1) ? 1 : numberOfSteps;
		time = (duration < time) ? duration : time;
	}
	
	/** Sets the program enumeration of the target. **/
	public void setProgram(PathProgramEnum programEnum) {
		this.programEnum = programEnum;
		program = programEnum.getProgram();
	}
	
	/** Returns the program enumeration of the target. **/
	public PathProgramEnum getProgram() {
		return programEnum;
	}

	@Override public int getCycleDuration() {
		return duration;
	}

	@Override public void play() {
		state = State.PLAY;
	}
	
	@Override public void stop() {
		state = State.STOP;
	}
	
	@Override public void rewind() {
		state = State.REWIND;
	}
	
	@Override public boolean isPlaying() {
		return (state == State.PLAY);
	}
	
	@Override public boolean isRewinding() {
		return (state == State.REWIND);
	}
	
	@Override public void seek(int time) {
		this.time = (duration < time) ? duration : time;
	}
	
	@Override public int pos() {
		return time;
	}
	
	@Override public void onStep(WorldArea world) {
		if (	(state == State.PLAY || state == State.REWIND) &&
				path != null && program != null) {
			final double position = program.run(this);
			path.getPoint(position, dP);
			dP.sub(getPosition());
			setVelocity(dP.x,dP.y,dP.z);

			if (state == State.PLAY) {
				if (time < duration)
					time++;
				else
					stop();
			} else {
				if (time > 0)
					time--;
				else
					stop();
			}
		} else
			setVelocity(0,0,0);
	}
	
	@Override public void setEnable(boolean enable, boolean instant) {
		if (enable && !instant)
			play();
		else {
			stop();
			if (instant)
				seek(enable ? getCycleDuration() : 0);
		}
	}

	@Override public boolean isEnabled() {
		return isPlaying() || isRewinding();
	}
	
	@Override public String getName() {
		return worldObjectName;
	}

	@Override public WorldObjectDeflation getDeflation() {
		return new TargetDeflation(0,this);
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Creates the appearance of this target. **/
	private static final Appearance3D createAppearance(AbstractEnvironment env) {
		if (env.isDebugMode()) {
			EllipsoidAppearance3D appearance = new EllipsoidAppearance3D();
			appearance.setColor(Color.red);
			return appearance;
		} else
			return new NoAppearance3D();
	}

}
